using System;
using System.Collections.Generic;
using System.Text;
using Interop = Microsoft.Feeds.Interop;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Collections.Specialized;
using System.Collections;

namespace Ricciolo.PaperBoy.Feeds
{
    public class FeedFolder : FeedBase, IFeed, IDelete, IFeedFolder
    {

        internal Interop.IFeedFolder feedFolder;
        internal Interop.IFeedFolderEvents_Event watcher;

        private FeedFolder _parent;
        private FeedBaseCollection _foldersAndFeeds;
        private MergedCollection _items;

        internal FeedFolder(FeedsManager feedsManager, string path)
            : base(feedsManager, path)
        {

            // Intercept self folder events
            this.watcher = (Interop.IFeedFolderEvents_Event)this.feedFolder.GetWatcher(Interop.FEEDS_EVENTS_SCOPE.FES_SELF_AND_CHILDREN_ONLY, Interop.FEEDS_EVENTS_MASK.FEM_FOLDEREVENTS);
            this.watcher.FolderRenamed += new Microsoft.Feeds.Interop.IFeedFolderEvents_FolderRenamedEventHandler(watcher_FolderRenamed);
            this.watcher.FolderItemCountChanged += new Microsoft.Feeds.Interop.IFeedFolderEvents_FolderItemCountChangedEventHandler(watcher_FolderItemCountChanged);
            this.watcher.FolderAdded += new Microsoft.Feeds.Interop.IFeedFolderEvents_FolderAddedEventHandler(watcher_FolderAdded);
            this.watcher.FolderDeleted += new Microsoft.Feeds.Interop.IFeedFolderEvents_FolderDeletedEventHandler(watcher_FolderDeleted);
            this.watcher.FolderMovedFrom += new Microsoft.Feeds.Interop.IFeedFolderEvents_FolderMovedFromEventHandler(watcher_FolderMovedFrom);
            this.watcher.FeedAdded += new Microsoft.Feeds.Interop.IFeedFolderEvents_FeedAddedEventHandler(watcher_FeedAdded);
            this.watcher.FeedDeleted += new Microsoft.Feeds.Interop.IFeedFolderEvents_FeedDeletedEventHandler(watcher_FeedDeleted);
            this.watcher.FeedMovedFrom += new Microsoft.Feeds.Interop.IFeedFolderEvents_FeedMovedFromEventHandler(watcher_FeedMovedFrom);
        }

        void watcher_FeedMovedFrom(string Path, string oldPath)
        {
            Feed f = this.feedsManager.GetFeedByPathFromCache(oldPath) as Feed;
            if (f != null)
            {
                FeedFolder oldParent = f.Parent;
                f.Path = Path;

                // Remove from main list
                this.feedsManager.allfeeds.Remove(oldPath);

                // Remove from old parent
                oldParent.FoldersAndFeeds.Remove(f);
                // Add to new parent
                f.Parent.FoldersAndFeeds.Add(f);

                this.feedsManager.allfeeds[Path] = new WeakReference(f);

            }
        }

        void watcher_FeedDeleted(string Path)
        {
            FeedBase f = this.feedsManager.GetFeedByPathFromCache(Path);
            // Remove from collection
            if (f != null)
            {
                // Remove from main list
                this.feedsManager.allfeeds.Remove(Path);

                ((Feed)f).Parent.FoldersAndFeeds.Remove(f);
            }
        }

        void watcher_FeedAdded(string Path)
        {
            this.FoldersAndFeeds.Add(new Feed(this.feedsManager, Path));
        }

        #region Handlers

        void watcher_FolderMovedFrom(string Path, string oldPath)
        {
            FeedFolder f = this.feedsManager.GetFeedByPathFromCache(oldPath) as FeedFolder;
            if (f != null)
            {
                // Remove from main list
                this.feedsManager.allfeeds.Remove(oldPath);
                // Remove from collection
                f.Parent.FoldersAndFeeds.Remove(f);
                f.Path = Path;
                f.FoldersAndFeeds.Refresh();
                f.Parent.FoldersAndFeeds.Add(f);
                this.feedsManager.allfeeds[Path] = new WeakReference(f);
            }
        }

        void watcher_FolderRenamed(string Path, string oldPath)
        {
            this.Path = Path;

            // Invalidate properties
            this.OnPropertyChanged("Name");
            this.OnPropertyChanged("Path");
        }

        void watcher_FolderDeleted(string Path)
        {
            FeedBase f = this.feedsManager.GetFeedByPathFromCache(Path);
            // Remove from collection
            if (f != null)
            {
                // Remove from main list
                this.feedsManager.allfeeds.Remove(Path);

                ((FeedFolder)f).Parent.FoldersAndFeeds.Remove(f);
            }
        }

        void watcher_FolderAdded(string Path)
        {
            this.FoldersAndFeeds.Add(new FeedFolder(this.feedsManager, Path));
        }

        void watcher_FolderItemCountChanged(string Path, int itemCountType)
        {
            this.Refresh();

            // Invalidate properties
            this.OnPropertyChanged("TotalItemCount");
            this.OnPropertyChanged("UnreadItemCount");
            this.OnPropertyChanged("HasUnreadItems");
        }

        #endregion

        #region Properties

        public override string Type
        {
            get { return typeof(FeedFolder).Name; }
        }

        /// <summary>
        /// Retrieves the total (aggregated) number of items
        /// </summary>
        public int TotalItemCount
        {
            get
            {
                try
                {
                    return this.feedFolder.TotalItemCount;
                }
                catch (Exception e)
                {
                    // TODO: handle
                    Debug.WriteLine(e.Message, this.Path);
                    return 0;
                }
            }
        }

        public bool HasUnreadItems
        {
            get { return this.UnreadItemCount > 0; }
        }

        /// <summary>
        /// Retrieves the total (aggregated) number of unread items
        /// </summary>
        public int UnreadItemCount
        {
            get
            {
                try
                {
                    return this.feedFolder.TotalUnreadItemCount;
                }
                catch (Exception e)
                {
                    // TODO: handle
                    Debug.WriteLine(e.Message, this.Path);
                    return 0;
                }
            }
        }

        /// <summary>
        /// Retrieves the collection of subfolders and feeds in this folder
        /// </summary>
        public FeedBaseCollection FoldersAndFeeds
        {
            get
            {
                if (_foldersAndFeeds == null)
                    lock (this)
                    {
                        if (_foldersAndFeeds == null)
                            this._foldersAndFeeds = new FeedBaseCollection(this);
                    }
                return _foldersAndFeeds;
            }
        }

        /// <summary>
        /// Retrieves all items of all feeds in current folder
        /// </summary>
        public MergedCollection Items
        {
            get
            {
                if (_items == null)
                {
                    List<INotifyCollectionChanged> feeds = new List<INotifyCollectionChanged>();
                    if (this.FoldersAndFeeds.Count == 0)
                        this.FoldersAndFeeds.Refresh();

                    // Get all feeds list
                    foreach (FeedBase fb in this.FoldersAndFeeds)
                        if (fb is Feed)
                            feeds.Add(((Feed)fb).Items);

                    _items = new MergedCollection(feeds.ToArray());
                }

                return _items;
            }
        }

        IList IFeed.Items
        {
            get { return this.Items; }
        }

        IFeedFolder IFeed.Parent
        {
            get { return this.Parent; }
        }

        /// <summary>
        /// Retrieves the parent object
        /// </summary>
        public FeedFolder Parent
        {
            get { return this._parent; }
        }

        /// <summary>
        /// Retrieves the display name of the feed or folder
        /// </summary>
        public string Name
        {
            get { return this.feedFolder.Name; }
            set { this.feedFolder.Rename(value); }
        }

        /// <summary>
        /// Checks to see whether the current folder is the root folder
        /// </summary>
        public bool IsRoot
        {
            get { return this.feedFolder.IsRoot; }
        }

        #endregion

        /// <summary>
        /// Deletes the feed, folder, or item
        /// </summary>
        public void Delete()
        {
            this.feedFolder.Delete();
        }

        /// <summary>
        /// Creates a new subfolder in the folder
        /// </summary>
        /// <param name="folderName"></param>
        public void CreateSubfolder(string folderName)
        {
            this.feedFolder.CreateSubfolder(folderName);
        }

        protected override void Refresh()
        {
            this.feedFolder = (Interop.IFeedFolder)feedsManager.feedsManager.GetFolder(this.Path);
            // Get the parent
            if (this.Path.Length > 0)
                this._parent = (FeedFolder)this.feedsManager.GetFeedByPathFromCache(((Interop.IFeedFolder)this.feedFolder.Parent).Path);
            this.OnPropertyChanged("Parent");
        }

    }

    public class FeedBaseCollection : ObservableCollectionEx<FeedBase>, IRefresh
    {

        private readonly FeedFolder _folder;

        internal FeedBaseCollection(FeedFolder folder)
        {
            if (folder == null)
                throw new ArgumentNullException("folder");

            this._folder = folder;
        }

        public FeedFolder Folder
        {
            get { return _folder; }
        }

        public void Refresh()
        {
            using (this.StartDeferRefresh(true))
            {
                this.Clear();
                Debug.WriteLine("Refresh folder " + this.Folder.Path);

                // Populate collection
                foreach (Interop.IFeedFolder f in (Interop.IFeedsEnum) this.Folder.feedFolder.Subfolders)
                {
                    FeedFolder ff = new FeedFolder(Folder.feedsManager, f.Path);
                    this.Add(ff);
                }

                foreach (Interop.IFeed f in (Interop.IFeedsEnum) this.Folder.feedFolder.Feeds)
                {
                    Feed ff = new Feed(Folder.feedsManager, f.Path);
                    this.Add(ff);
                }

            }
        }

    }
}
